/*
	freezer -- contains methods that convert a file type into a file_data type, ready to be passed to userspace for freezing
*/

#include <linux/slab.h> 
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/uio.h>
#include <linux/smp_lock.h>
#include <linux/fsnotify.h>
#include <linux/security.h>
#include <linux/module.h>
#include <linux/syscalls.h>
#include <linux/pagemap.h>
#include <linux/list.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <linux/freezer.h>
  
int HAS_DATA = 0;
int INIT_FREEZE = 0;

struct file_data {
  unsigned int unique_id;
  int newFile;
  int regFile;
  char path_name[128];
};

struct file_data *transfer;

int freezer_hasData() {
  return HAS_DATA;
}

//Return struct to pass to user application
struct file_data * get_frozen_file() {
  HAS_DATA = 0;
  return transfer;
}

void begin_freeze() {
  INIT_FREEZE = 1;
}

/* interceptor(*__file, nFile)
   	called when user application modifies/creates file

   	input: file to modify by kernel, newFile?
		output: 1 -> "OK"

		out var: (global) transfer, set with attributes of __file
*/
int interceptor(struct file *__file, int nFile) {
	//Thawed? Don't backup file
	//	(most likely called during bootup)
  if (INIT_FREEZE == 0) return 1;

	//Is it file (not device, socket, directory)?
  if (S_ISREG(__file->f_dentry->d_inode->i_mode)) {
		//Set (global) transfer with extracted attributes from __file
                //also check to see if this file was already frozen
    acquire_path(__file);
    transfer->newFile = nFile;
    transfer->regFile = 1;
    transfer->unique_id = __file->f_dentry->d_name.hash;
    HAS_DATA = 1;
	
	//Directory?
  } else if (S_ISDIR(__file->f_dentry->d_inode->i_mode)) {
    acquire_path(__file);
    transfer->newFile = nFile;
    transfer->regFile = 0;
    transfer->unique_id = __file->f_dentry->d_name.hash;
    HAS_DATA = 1;
  }

  return 1;
}

/* acquire_path(__file)	
	input: a file (not FILE)		
	out var: (global) transfer
*/
void acquire_path(struct file *__file) {
  struct dentry *temp;

  char temp_name[128];

  //Make new file_data struct to pass to user
  transfer = (struct file_data *)kmalloc(sizeof(struct file_data), __GFP_NORETRY);

  temp = __file->f_dentry;

  //Copy filename for output
  strcpy(transfer->path_name, temp->d_iname);
  temp = temp->d_parent;

  //Follow i_node parents back to '/'
  while (strcmp(temp->d_iname, "/")) {
    strcpy(temp_name, temp->d_iname);
    strcat(temp_name, "/");
    strcat(temp_name, transfer->path_name);
    strcpy(transfer->path_name, temp_name);
    temp = temp->d_parent;
  }
}
